package com.ysdz.tibetan.tibetanmini.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.ysdz.tibetan.tibetanmini.product.beans.*;
import com.ysdz.tibetan.tibetanmini.product.dao.ProductImgMapper;
import com.ysdz.tibetan.tibetanmini.product.dao.ProductMapper;
import com.ysdz.tibetan.tibetanmini.product.dao.ProductParamsMapper;
import com.ysdz.tibetan.tibetanmini.product.service.ProductService;
import com.ysdz.tibetan.tibetanmini.utils.ElasticSearchConst;
import com.ysdz.tibetan.tibetanminicommons.common.ReturnMsg;
import com.ysdz.tibetan.tibetanminicommons.generalService.impl.AbstractService;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private ProductImgMapper productImgMapper;
    @Autowired
    private ProductParamsMapper productParamsMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public ReturnMsg selectProductsByKeyword(String keyword) {
        SearchRequest searchRequest = new SearchRequest(ElasticSearchConst.ES_PRODUCT_INDEX);
        // 构建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 查询条件构建  term 精确匹配   match 模糊匹配
        MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("productName", keyword);
        searchSourceBuilder.query(matchQuery);
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        searchRequest.source(searchSourceBuilder);

        // 发送请求
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 封装数据
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit hit : searchResponse.getHits().getHits()) {
            list.add(hit.getSourceAsMap());
        }
        return new ReturnMsg(ReturnMsg.SUCCESS_CODE, "成功查询!", list);
    }

    @Override
    public ReturnMsg selectProductsByKeywordByPaging(String keyword, String cid, Integer pagenum, Integer pagesize) {
        if(pagenum <= 1) pagenum = 1;

        SearchRequest searchRequest = new SearchRequest(ElasticSearchConst.ES_PRODUCT_INDEX);
        // 构建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 查询条件构建  term 精确匹配   match 模糊匹配
        MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("productName", keyword);
        searchSourceBuilder.query(matchQuery);
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        // 分页
        searchSourceBuilder.from(pagenum);
        searchSourceBuilder.size(pagesize);
        searchRequest.source(searchSourceBuilder);

        // 发送请求
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 封装数据
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit hit : searchResponse.getHits().getHits()) {
            list.add(hit.getSourceAsMap());
        }
        return new ReturnMsg(ReturnMsg.SUCCESS_CODE, "成功查询!", list);
    }

    @Override
    public ReturnMsg getAllProducts() {
        String key = "product_all";
        ValueOperations<String, List<Product>> operations = redisTemplate.opsForValue();
        boolean hadKey = redisTemplate.hasKey(key);
        if(hadKey){
            List<Product> productList = operations.get(key);
            return new ReturnMsg(ReturnMsg.SUCCESS_CODE, "查找成功！", productList);
        }else{
            // 在 elasticSearch 中查找数据
            List<Product> productList = productMapper.selectAll();

            // 将数据存储至 ElasticSearch
            BulkRequest bulkRequest = new BulkRequest();
            bulkRequest.timeout("10s");
            // 将数据放入请求
            for (int i = 0; i < productList.size(); i++){
                bulkRequest.add(
                        new IndexRequest(ElasticSearchConst.ES_PRODUCT_INDEX)
                        .id(productList.get(i).getProductId())
                        .source(JSON.toJSONString(productList.get(i)), XContentType.JSON));
            }
            //  发送请求
            BulkResponse bulkResponse = null;
            try {
                bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 存入 Redis
            operations.set(key, productList);
            return new ReturnMsg(ReturnMsg.SUCCESS_CODE, "成功！", productList);
        }
    }


    @Override
    public ReturnMsg selectProductById(String id) {
        String key = "product_detail_" + id;
        ValueOperations<String, ProductVO> operations = redisTemplate.opsForValue();
        boolean hadKey = redisTemplate.hasKey(key);
        if(hadKey){
            ProductVO productVO = operations.get(key);
            return new ReturnMsg(ReturnMsg.SUCCESS_CODE, "查找成功！", productVO);
        }else{
            // 在 elastcisearch 中查找数据
            ProductVO productVO = getProductDetail(id);

            // 将数据存储至 ElasticSearch
            IndexRequest indexRequest = new IndexRequest("ti_product");
            indexRequest.id(productVO.getProductId());
            indexRequest.timeout(TimeValue.timeValueSeconds(1));
            // 将数据放入请求
            IndexRequest source = indexRequest.source(JSON.toJSONString(productVO), XContentType.JSON);
            //  发送请求
            IndexResponse index = null;
            try {
                index = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 存入 Redis
            operations.set(key, productVO);
            return new ReturnMsg(ReturnMsg.SUCCESS_CODE, "成功！", productVO);
        }
    }

    private Object getProductFromElasticSearch(String id){
        GetRequest getRequest = new GetRequest("ti_product", id);
        GetResponse documentFields = null;
        try {
            documentFields = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        Map<String, Object> source = documentFields.getSourceAsMap();
        return source.get("_source");
    }

    // 判断 id 的商品是否存在于 Elasticsearch
    private boolean productIsExist(String id){
        GetRequest getRequest = new GetRequest("ti_product", id);
        // 去除 _source  的上下文
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");
        boolean isExist = false;
        try {
            isExist = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return isExist;
    }

    private ProductVO getProductDetail(String id){
        // 创建返回商品详情数据
        ProductVO productVO = new ProductVO();
        // 从数据库中查询 id 商品
        Example example = new Example(Product.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("productId", id);
        List<Product> productList = productMapper.selectByExample(example);
        Product product = productList.get(0);
        productVO.setProductId(product.getProductId());
        productVO.setProductName(product.getProductName());
        productVO.setProductNameTibetan(product.getProductNameTibetan());
        productVO.setCategoryId(product.getCategoryId());
        productVO.setRootCategoryId(product.getRootCategoryId());
        productVO.setSoldNum(product.getSoldNum());
        productVO.setContent(product.getContent());

        // 根据 productId 查询商品详情数据
        Example example1 = new Example(ProductParams.class);
        Example.Criteria criteria1 = example1.createCriteria();
        criteria1.andEqualTo("productId",productVO.getProductId());
        List<ProductParams> productParamsList = productParamsMapper.selectByExample(example1);
        if(productParamsList.size() > 0){
            productVO.setProductParams(productParamsList.get(0));
        }else{
            productVO.setProductParams(null);
        }

        // 根据 productId 查询商品图片
        Example example2 = new Example(ProductImg.class);
        Example.Criteria criteria2 = example2.createCriteria();
        criteria2.andEqualTo("itemId",productVO.getProductId());
        List<ProductImg> productImgList = productImgMapper.selectByExample(example2);
        if(productImgList.size() > 0){
            productVO.setPics(productImgList);
        }else{
            productVO.setPics(null);
        }

        return productVO;
    }

}
